/**
 * 
 */
package ch.comtools.servermanager.ssh.core;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import ch.comtools.jsch.Channel;
import ch.comtools.jsch.JSch;
import ch.comtools.jsch.JSchException;
import ch.comtools.jsch.Session;
import ch.comtools.servermanager.ssh.SSH;

/**
 * @author Roger Dudler <roger.dudler@gmail.com>
 * @version $Id: SecureChannelFactory.java 56 2007-04-19 15:00:55Z roger.dudler $
 */
public class SecureChannelFactory {
	
	private static SecureChannelFactory instance;
	private Map<SessionInfo, Session> sessions = new HashMap<SessionInfo, Session>();
	private int sessionCount = 0;

	/**
	 * Private constructor.
	 */
	private SecureChannelFactory() {
		// NOOP
	}

	/**
	 * Get an instance of this class.
	 * @return instance
	 */
	public static SecureChannelFactory getInstance() {
		if (instance == null) {
			instance = new SecureChannelFactory();
		}
		return instance;
	}
	
	/**
	 * Create a new {@link Channel}.
	 * @param host
	 * @param user
	 * @param pass
	 * @param port
	 * @param type
	 * @return
	 * @throws JSchException 
	 */
	public Channel createChannel(String host, String user, String pass, int port, String type) throws JSchException {
		SessionInfo info = new SessionInfo(host, user, pass, port);
		Session session = this.getSession(info);
		Channel channel = session.openChannel(type);
		return channel;
	}
	
	/**
	 * Create a new {@link Channel}.
	 * @param host
	 * @param user
	 * @param pass
	 * @param port
	 * @return
	 * @throws JSchException 
	 */
	public Channel createChannel(String host, String user, String pass, int port) throws JSchException {
		SessionInfo info = new SessionInfo(host, user, pass, port);
		Session session = this.getSession(info);
		Channel channel = session.openChannel(SSH.CHANNEL_TYPE_SHELL);
		return channel;
	}

	/**
	 * Create a new {@link Channel}.
	 * @param host
	 * @param user
	 * @param pass
	 * @return
	 * @throws JSchException 
	 */
	public Channel createChannel(String host, String user, String pass) throws JSchException {
		SessionInfo info = new SessionInfo(host, user, pass);
		Session session = this.getSession(info);
		Channel channel = session.openChannel(SSH.CHANNEL_TYPE_SHELL);
		return channel;
	}
	
	/**
	 * @param host
	 * @param user
	 * @param pass
	 * @param type
	 * @return
	 * @throws JSchException
	 */
	public Channel createChannel(String host, String user, String pass, String type) throws JSchException {
		SessionInfo info = new SessionInfo(host, user, pass);
		Session session = this.getSession(info);
		Channel channel = session.openChannel(type);
		return channel;
	}
	
	/**
	 * Create a new {@link Channel}.
	 * @param info
	 * @param type
	 * @return
	 * @throws JSchException
	 */
	public Channel createChannel(SessionInfo info, String type) throws JSchException {
		Session session = this.getSession(info);
		Channel channel = session.openChannel(type);
		return channel;
	}
	
	/**
	 * Create a new session.
	 * @param info
	 * @return
	 * @throws JSchException 
	 */
	private Session createSession(SessionInfo info) throws JSchException {
		JSch jsch = new JSch();
		// jsch.setKnownHosts("/home/foo/.ssh/known_hosts");

		// create session
		Session session = jsch.getSession(info.getUser(), info.getHost(), info.getPort());
		session.setPassword(info.getPassword());

		// configure session
		Hashtable<String, String> config = new Hashtable<String, String>();
		config.put("StrictHostKeyChecking", "no");
		session.setConfig(config);
		
		// connect session
		session.connect();
		
		// update session count
		this.sessionCount++;
		
	    // session.connect(30000); // making a connection with timeout.
		return session;
	}
	
	/**
	 * Get a session for given information.
	 * @param host
	 * @param user
	 * @param pass
	 * @param port
	 * @return
	 * @throws JSchException 
	 */
	public Session getSession(String host, String user, String pass, int port) throws JSchException {
		SessionInfo info = new SessionInfo(host, user, pass, port);
		return this.getSession(info);
	}
	
	/**
	 * Get a session for given information.
	 * @param host
	 * @param user
	 * @param pass
	 * @return
	 * @throws JSchException 
	 */
	public Session getSession(String host, String user, String pass) throws JSchException {
		SessionInfo info = new SessionInfo(host, user, pass);
		return this.getSession(info);
	}
	
	/**
	 * Get a session for given information.
	 * @param info
	 * @return
	 * @throws JSchException 
	 */
	public Session getSession(SessionInfo info) throws JSchException {
		if (!this.isSessionAvailable(info)) {
			this.sessions.put(info, this.createSession(info));
		}
		return this.sessions.get(info);
	}
	
	/**
	 * Check if a usable {@link Session} exists for given {@link SessionInfo}.
	 * @param info session information, see {@link SessionInfo}
	 * @return <code>true</code>, if a {@link Session} is available, <code>false</code>, if not
	 */
	public boolean isSessionAvailable(SessionInfo info) {
		return this.sessions.containsKey(info);
	}
	
	/**
	 * @return
	 */
	public String getSessionId() {
		return (this.sessionCount + 1) + "";
	}

}
